package socket;

import jdbc.DBUtil;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server {
    private ServerSocket serverSocket;

    //private List<PrintWriter>allOut=new ArrayList<>();//存放所有客户端的输出流，用于广播消息
    //存放客户端输出流，key是昵称，value是输出流
    private Map<String ,PrintWriter> allOut=new HashMap<>();
    //线程池
    private ExecutorService threadPool;
    public Server(){
        try {
            System.out.println("正在启动服务器...");
            serverSocket=new ServerSocket(8088);
            threadPool= Executors.newFixedThreadPool(50);
            System.out.println("服务器启动成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void start(){
        try {
            System.out.println("等待客户端连接...");
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println("一个客户端连接了");
                //启动会一个线程处理客户端的请求
                ClientHandler handler=new ClientHandler(socket);
/*                Thread t=new Thread(handler);
                t.start();*/
                threadPool.execute(handler);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }
    private class ClientHandler implements Runnable{
        private Socket socket;
        public String host;//记录客户端地址
        public String nickname;//记录客户端昵称
        public ClientHandler(Socket socket){
            this.socket= socket;
            //通过socket获取远端计算机地址信息，就是客户端
            host=socket.getInetAddress().getHostAddress();
        }
        @Override
        public void run() {
            PrintWriter pw=null;
            try {
                InputStream in = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
                BufferedReader br = new BufferedReader(isr);
                //首先单独读取来自客户端发送过来的一行字符串，为该客户端的昵称
                nickname=br.readLine();

                OutputStream out=socket.getOutputStream();
                OutputStreamWriter osw=new OutputStreamWriter(out, StandardCharsets.UTF_8);
                BufferedWriter bw=new BufferedWriter(osw);
                pw=new PrintWriter(bw,true);
                //向集合中添加元素要添加同步机制，保证多个线程不能同时添加
                synchronized (allOut) {
                    allOut.put(nickname,pw);//将该客户端的输出流存入共享集合
                    //通知所有客户端该用户上线
                    sendMessage(nickname + "[" + host + "]" + "加入聊天室，当前在线人数" + allOut.size());
                }
                String message;
                while ((message = br.readLine() )!= null) {
                    //如果聊天内容以@开头，则私聊
                    if (message.startsWith("@")) {
                        sendMessage(message);
                    } else {//否则是群聊
                        //昵称[ip]说：消息
                        sendMessage(nickname+"["+host+"]"+"说：" + message);
                        saveMessage(nickname,null,message);
                    }
                }
            } catch (IOException e) {
                //e.printStackTrace();异常处理：假装没有
            }finally {
                //添加同步机制，并与添加动作互斥
                synchronized (allOut) {
                    //1.处理客户端下线后的操作
                    allOut.remove(nickname);
                }
                //2.通知所有客户端该用户下线
                sendMessage(nickname+"["+host+"]"+"离开聊天室，当前在线人数"+allOut.size());
                try {
                    socket.close();
                }catch (IOException e){
                    e.printStackTrace();
                }

            }
        }
        //私聊功能
        private void sendMessageToSomeone(String message){
            if (message.matches("@.+:.+")){
                String toNickname=message.substring(1,message.indexOf(":"));//获取消息中的昵称
                if (allOut.containsKey(toNickname)) {//判断对方是否在线
                    PrintWriter pw=allOut.get(toNickname);//根据昵称获取输出流
                    //根据消息截取出私聊内容
                    String tomessage=message.substring(message.indexOf(":")+1);
                    pw.println("【私聊】"+nickname+"说："+tomessage);
                    //将消息保存到数据库
                    saveMessage(nickname,toNickname,tomessage);
                } else {
                    PrintWriter pw=allOut.get(nickname);
                    pw.println("用户["+toNickname+"]不在线");
                }
            }else{
                PrintWriter pw=allOut.get(nickname);
                pw.println("私聊格式不正确");
            }
        }
        //群聊功能
        private void sendMessage(String message){
            //昵称[ip]说：消息
            System.out.println(message);
            //遍历allout集合，将消息发送给所有客户端
            //新循环集合就是迭代器，因为迭代器要求遍历过程中不可以使用集合方法增删元素，因此为了
            //保证遍历正确，防止其他线程增删，这里要和其他线程的增删动作互斥
            synchronized (allOut) {
                for (PrintWriter a : allOut.values()) {//新循环遍历集合就是迭代器
                    a.println(message);
                }
            }
        }
        private void saveMessage(String fromUser,String toUser,String info){
            try(
                    Connection con= DBUtil.getConnection();
                    ) {
                String sql="INSERT INTO chatinfo(from_user,to_user,info) VALUES(?,?,?)";
                PreparedStatement ps= con.prepareStatement(sql);
                ps.setString(1,fromUser);
                ps.setString(2,toUser);
                ps.setString(3,info);
                ps.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            }

        };
    }
}
